package com.molichuxing.gateway.api.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.molichuxing.framework.contants.RedisKeyConst;
import com.molichuxing.framework.dto.Paged;
import com.molichuxing.framework.dto.RequestRootDto;
import com.molichuxing.framework.dto.TabDto;
import com.molichuxing.framework.property.OperatorTypeEnum;
import com.molichuxing.framework.result.ResponseCodeEnum;
import com.molichuxing.framework.result.ResponseResult;
import com.molichuxing.framework.utils.CollectionUtils;
import com.molichuxing.framework.utils.Md5Util;
import com.molichuxing.framework.utils.RedisUtil;
import com.molichuxing.framework.utils.StringUtil;
import com.molichuxing.gateway.utils.DesenseUtil;
import com.molichuxing.gateway.utils.RedisPermissionUtil;
import com.molichuxing.gateway.utils.TokenUtil;
import com.molichuxing.gateway.utils.entity.UserEntity;
import com.molichuxing.services.infrastructure.dto.response.IovMileageDto;
import com.molichuxing.services.infrastructure.dto.response.response.GroupDto;
import com.molichuxing.services.infrastructure.dto.response.response.ResourceDto;
import com.molichuxing.services.infrastructure.service.GroupService;
import com.molichuxing.services.infrastructure.service.ResourceService;
import com.molichuxing.services.property.GroupSystemTypeEnum;
import com.molichuxing.services.property.StatusEnum;
import com.molichuxing.services.property.SystemTypeEnum;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 登录过滤
 *
 * @author zoumingyu
 * @date 2019年8月8日
 */
@Aspect
@Order(1)
public class TokenAspect {
    private final static Logger logger = LoggerFactory.getLogger("[op-gateway登录过滤]");

    @Resource
    private TokenUtil tokenUtil;

    private List<String> excludeMapping;

    private List<String> desenseExcludeMapping;

    private List<String> tabExcludeMapping;

    @Resource
    private ResourceService resourceService;

    @Resource
    private RedisPermissionUtil redisPermissionUtil;

    // 环境
    @Value("${profiles.active}")
    private String active;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private GroupService groupService;

    private List<String> permissionExcludeMapping;

    public void setExcludeMapping(List<String> excludeMapping) {
        this.excludeMapping = excludeMapping;
    }

    public void setDesenseExcludeMapping(List<String> desenseExcludeMapping) {
        this.desenseExcludeMapping = desenseExcludeMapping;
    }

    public void setPermissionExcludeMapping(List<String> permissionExcludeMapping) {
        this.permissionExcludeMapping = permissionExcludeMapping;
    }

    public void setTabExcludeMapping(List<String> tabExcludeMapping) {
        this.tabExcludeMapping = tabExcludeMapping;
    }

    @Pointcut("execution(public * com.molichuxing.gateway.api.services.impl..*(..))")
    public void token() {
    }

    @Around("token()")
    public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "*");
        response.setHeader("Access-Control-Allow-Headers",
                "Origin,Content-Type,Accept,channel,token,workertoken,bondtoken,X-Requested-With");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        // 忽略OPTIONS预校验请求
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            return joinPoint.proceed();
        }

        String uri = request.getRequestURI();
        // 放过不拦截接口
        for (String exclude : excludeMapping) {
            if (uri.equalsIgnoreCase(exclude)) {
                return joinPoint.proceed();
            }
        }
        // 用户token
        String token = request.getHeader("token");
        if (StringUtils.isBlank(token)) {
            return noLogin();
        }

        // 根据token从redis中获取操作人相关信息
        UserEntity user = tokenUtil.getUser(token);
        if (user == null) {
            return noLogin();
        }

        //判断ssotoken是否一致，不一致，那么等于被挤掉
        if (!user.getSsoToken().equals(token)) {
            return pushOut();
        }

        tokenUtil.expire(token);

        //权限认证
        for (String exclude : permissionExcludeMapping) {
            if (uri.equalsIgnoreCase(exclude)) {
                return joinPoint.proceed();
            } else if (exclude.endsWith("/*")) {
                exclude = exclude.substring(0, exclude.length() - 2);
                if (uri.startsWith(exclude)) {
                    return joinPoint.proceed();
                }
            }
        }

        if (user.getGroupId() != null && user.getGroupId() > 0) {
            String key =  RedisKeyConst.OP_GROUP_PERMISSION + Md5Util.md5Hex(active) +"."+  Md5Util.md5Hex(user.getGroupId()+"");
            boolean exists = redisUtil.exists(key);
            if (!exists) {
                redisPermissionUtil.resourceToRedis(user.getGroupId(), GroupSystemTypeEnum.MOLI);
            }
            String value = redisUtil.hget(key, uri);;
            if (StringUtil.isEmpty(value))
                return noPermission();
        } else {
            return noPermission();
        }
        //权限认证end


        Object[] objArray = joinPoint.getArgs();
        for (Object obj : objArray) {
            if (obj != null && obj instanceof RequestRootDto) {
                RequestRootDto dto = (RequestRootDto) obj;
                // 赋值操作人相关信息
                dto.setOperateId(user.getAccountId()); // 操作人id
                dto.setOperateName(user.getRealName()); // 操作人姓名
                dto.setOperateDate(LocalDateTime.now()); // 操作时间
                dto.setOperatorType(OperatorTypeEnum.OP); // 操作人类型-op
            }
        }

        return joinPoint.proceed();


    }

    @AfterReturning(returning = "returnValue", pointcut = "token()")
    public void doAfterReturning(Object returnValue) {
        long threadId = Thread.currentThread().getId();
        ObjectMapper mapper = new ObjectMapper();
        try {
            logger.info("[" + threadId + "]" + "返回内容：" + mapper.writeValueAsString(returnValue));
        } catch (Exception ex) {
            logger.info("[" + threadId + "]" + "返回内容：对返回值序列化时发生异常。");
        }

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();//request

        String uri = request.getRequestURI();
        // 放过不验证token接口
        for (String exclude : excludeMapping) {
            if (uri.equalsIgnoreCase(exclude)) {
                return;
            }
        }
        // 放过不脱敏接口
        for (String exclude : desenseExcludeMapping) {
            if (uri.equalsIgnoreCase(exclude)) {
                return;
            } else if (exclude.endsWith("/*")) {
                exclude = exclude.substring(0, exclude.length() - 2);
                if (uri.startsWith(exclude)) {
                    return;
                }
            }
        }


        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();//获取response

        String token = request.getHeader("token");
        // 根据token从redis中获取操作人相关信息
        UserEntity user = tokenUtil.getUser(token);

        if (user != null && user.getGroupId() != null) {
            Map<String, String> desenseMap = redisPermissionUtil.getDesenseByGroupId(user.getGroupId()); //脱敏
            Map<String, String> sensitiveWordMap = redisPermissionUtil.getSensitiveWords(); //敏感词
            if ((desenseMap != null && desenseMap.size() > 0) || (sensitiveWordMap != null && sensitiveWordMap.size() > 0)) {
                //判断请求是否为成功
                if (returnValue != null && returnValue instanceof ResponseResult) {
                    ResponseResult result = (ResponseResult) returnValue;
                    //只判断返回code为200的数据
                    if (result.getCode().equals(ResponseCodeEnum.SUCCESS.getCode())) {
                        Object data = result.getData();
                        if (null != data) {
                            if (data instanceof List) {
                                if ((data != null) && (((List) data).size() > 0)) {
                                    //获取返回数据的Class类型
                                    Class<?> classes = ((ArrayList) (((ResponseResult) returnValue).getData())).get(0).getClass();
                                    try {
                                        result.setData(DesenseUtil.convertList((List) data, desenseMap, sensitiveWordMap, classes));
                                    } catch (Exception e) {
                                        logger.error("转换List格式错误。" + JSON.toJSONString(e));
                                        return;
                                    }
                                }
                            } else if (data instanceof Paged) {
                                Paged page = (Paged) data;
                                removeTab(page, uri, user);
                                if (page != null && CollectionUtils.isNotEmpty(page.getList())) {
                                    if (CollectionUtils.isNotEmpty(page.getList())) {
                                        try {
                                            List<Object> pageResultList = page.getList();
                                            if ((null != pageResultList) && (0 < pageResultList.size())) {
                                                Class<?> classes = pageResultList.get(0).getClass();
                                                List<Object> resultParam = DesenseUtil.convertList(pageResultList, desenseMap, sensitiveWordMap, classes);
                                                page.setList(resultParam);
                                                result.setData(page);
                                            }
                                        } catch (Exception e) {
                                            logger.error("转换List格式错误。" + e.getMessage(), e);
                                            return;
                                        }
                                    }
                                }
                            } else if (data instanceof Map) {
                                JSONObject dataJson = JSONObject.parseObject(JSON.toJSONString(data));
                                //判断是否返回的为Page类型
                                if ((null != dataJson.get("total")) && (null != dataJson.get("pages"))) {
                                    if (null != dataJson.getJSONArray("list")) {
                                        try {
                                            List<Object> pageResultList = ((Paged) (((ResponseResult) returnValue).getData())).getList();
                                            if ((null != pageResultList) && (0 < pageResultList.size())) {
                                                Class<?> classes = pageResultList.get(0).getClass();
                                                List<Object> resultParam = DesenseUtil.convertList(dataJson.getJSONArray("list"), desenseMap, sensitiveWordMap, classes);
                                                dataJson.put("list", resultParam);
                                                result.setData(dataJson);
                                            }
                                        } catch (Exception e) {
                                            logger.error("转换List格式错误。" + JSON.toJSONString(e));
                                            return;
                                        }
                                    }
                                } else if (dataJson instanceof Map) {
                                    Map<Object, Object> mapPrm = (Map) dataJson;
                                    try {
                                        result.setData(DesenseUtil.convertMap(mapPrm, desenseMap, sensitiveWordMap, returnValue));
                                    } catch (Exception e) {
                                        logger.error("转换List格式错误。" + JSON.toJSONString(e));
                                        return;
                                    }
                                } else {
                                    try {
                                        Class<?> classes = ((((ResponseResult) returnValue).getData())).getClass();
                                        result.setData(DesenseUtil.convertList(dataJson, desenseMap, sensitiveWordMap,  classes));
                                    } catch (Exception e) {
                                        logger.error("转换List格式错误。" + JSON.toJSONString(e));
                                        return;
                                    }
                                }
                            } else {
                                try {
                                    Class<?> classes = ((((ResponseResult) returnValue).getData())).getClass();
                                    result.setData(DesenseUtil.convertList(data, desenseMap, sensitiveWordMap, classes));
                                } catch (Exception e) {
                                    logger.error("转换List格式错误。" + JSON.toJSONString(e));
                                    return;
                                }

                            }
                        }
                    }
                    //转换json（解决时间转JSON后变成毫秒数）
                    String json = JSON.toJSONString(result, SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.WriteMapNullValue);
                    ServletOutputStream out = null;
                    try {
                        response.setCharacterEncoding("UTF-8");
                        response.setContentType("application/json;charset=" + "UTF-8");
                        out = response.getOutputStream();
                        out.write(json.getBytes(StandardCharsets.UTF_8));
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (null != out) {
                            try {
                                out.flush();
                                out.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }
    }

    private ResponseResult<String> noLogin() {
        return new ResponseResult<String>().error(ResponseCodeEnum.LOGIN_ERROR);
    }

    private ResponseResult<String> pushOut() {
        return new ResponseResult<String>().error(ResponseCodeEnum.LOGIN_ERROR.getCode(), "您的帐号在另一地点登录，您被迫下线");
    }

    private ResponseResult<String> noPermission() {
        return new ResponseResult<String>().error(ResponseCodeEnum.NO_PERMISSION);
    }


    private void removeTab(Paged page, String uri, UserEntity user) {
        try{
            if(tabExcludeMapping.contains(uri)) {
                return;
            }
            if (page != null && CollectionUtils.isNotEmpty(page.getTab())) {
                List<ResourceDto> resourceDtoList = resourceService.getResourceTabByUri(uri, SystemTypeEnum.OP.getValue(), user.getGroupId());
                if(null == resourceDtoList || resourceDtoList.size() == 0) {
                    page.setTab(null);
                }else {
                    List<TabDto> tabList = page.getTab();
                    Map<Integer, List<ResourceDto>> resourceDtoMap = resourceDtoList.stream().collect(Collectors.groupingBy(ResourceDto::getPid));
                    resourceDtoList.clear();
                    for(Integer key : resourceDtoMap.keySet()) {
                        List<ResourceDto> resourceList = resourceDtoMap.get(key);
                        for(TabDto tabDto : tabList) {
                            for(ResourceDto resourceDto : resourceList) {
                                if(tabDto.getName().equals(resourceDto.getPermissionName())&&tabDto.getType().equals(resourceDto.getSort())) {
                                    resourceDtoList.add(resourceDto);
                                    break;
                                }
                            }
                        }
                    }
                    List<TabDto> tabResult = new ArrayList<>();
                    for(TabDto tabDto : tabList) {
                        for(ResourceDto resourceDto : resourceDtoList) {
                            if(tabDto.getName().equals(resourceDto.getPermissionName())&&tabDto.getType().equals(resourceDto.getSort())) {
                                tabResult.add(tabDto);
                                break;
                            }
                        }
                    }
                    page.setTab(tabResult.size() == 0?null:tabResult);
                }
            }
        }catch (Exception e){
            logger.error("移除没有权限的标签异常e ： "+e);
        }
    }
}
